package com.hqd.ch03.v43.context.support;

import com.hqd.ch03.utils.ObjectUtils;
import com.hqd.ch03.v43.context.HierarchicalMessageSource;
import com.hqd.ch03.v43.context.MessageSource;
import com.hqd.ch03.v43.context.MessageSourceResolvable;
import com.hqd.ch03.v43.context.NoSuchMessageException;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

/**
 * 抽象国际化处理，用以定义处理顺序
 */
public abstract class AbstractMessageSource extends MessageSourceSupport implements HierarchicalMessageSource {
    /**
     * 父级消息处理
     */
    private MessageSource parentMessageSource;
    /**
     * 公共配置
     */
    private Properties commonMessages;

    private boolean useCodeAsDefaultMessage = false;

    @Override
    public MessageSource getParentMessageSource() {
        return this.parentMessageSource;
    }

    @Override
    public void setParentMessageSource(MessageSource parent) {
        this.parentMessageSource = parent;
    }

    protected Properties getCommonMessages() {
        return this.commonMessages;
    }

    public void setCommonMessages(Properties commonMessages) {
        this.commonMessages = commonMessages;
    }

    protected boolean isUseCodeAsDefaultMessage() {
        return this.useCodeAsDefaultMessage;
    }

    public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) {
        this.useCodeAsDefaultMessage = useCodeAsDefaultMessage;
    }

    @Override
    public final String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
        String msg = getMessageInternal(code, args, locale);
        if (msg != null) {
            return msg;
        }
        if (defaultMessage == null) {
            return getDefaultMessage(code);
        }
        return renderDefaultMessage(defaultMessage, args, locale);
    }

    @Override
    public final String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
        String msg = getMessageInternal(code, args, locale);
        if (msg != null) {
            return msg;
        }
        String fallback = getDefaultMessage(code);
        if (fallback != null) {
            return fallback;
        }
        throw new NoSuchMessageException(code, locale);
    }

    @Override
    public final String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
        String[] codes = resolvable.getCodes();
        if (codes != null) {
            for (String code : codes) {
                String message = getMessageInternal(code, resolvable.getArguments(), locale);
                if (message != null) {
                    return message;
                }
            }
        }
        String defaultMessage = getDefaultMessage(resolvable, locale);
        if (defaultMessage != null) {
            return defaultMessage;
        }
        throw new NoSuchMessageException(!ObjectUtils.isEmpty(codes) ? codes[codes.length - 1] : "", locale);
    }


    protected String getMessageInternal(String code, Object[] args, Locale locale) {
        /**
         * code为空，直接返回
         */
        if (code == null) {
            return null;
        }
        /**
         * 地区为空，获取默认地区
         */
        if (locale == null) {
            locale = Locale.getDefault();
        }
        Object[] argsToUse = args;
        /**
         * 关闭格式化且不包含参数
         */
        if (!isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {
            String message = resolveCodeWithoutArguments(code, locale);
            if (message != null) {
                return message;
            }
        } else {
            //处理参数
            argsToUse = resolveArguments(args, locale);
            MessageFormat messageFormat = resolveCode(code, locale);
            if (messageFormat != null) {
                synchronized (messageFormat) {
                    return messageFormat.format(argsToUse);
                }
            }
        }
        /**
         * 还没有获取到，则尝试去全局配置获取
         */
        Properties commonMessages = getCommonMessages();
        if (commonMessages != null) {
            String commonMessage = commonMessages.getProperty(code);
            if (commonMessage != null) {
                return formatMessage(commonMessage, args, locale);
            }
        }
        /**
         * 还是没有，则去父级获取
         */
        return getMessageFromParent(code, argsToUse, locale);
    }

    protected String getMessageFromParent(String code, Object[] args, Locale locale) {
        MessageSource parent = getParentMessageSource();
        if (parent != null) {
            if (parent instanceof AbstractMessageSource) {
                return ((AbstractMessageSource) parent).getMessageInternal(code, args, locale);
            } else {
                return parent.getMessage(code, args, null, locale);
            }
        }
        return null;
    }

    protected String getDefaultMessage(MessageSourceResolvable resolvable, Locale locale) {
        String defaultMessage = resolvable.getDefaultMessage();
        String[] codes = resolvable.getCodes();
        if (defaultMessage != null) {
            if (resolvable instanceof DefaultMessageSourceResolvable &&
                    !((DefaultMessageSourceResolvable) resolvable).shouldRenderDefaultMessage()) {
                return defaultMessage;
            }
            if (!ObjectUtils.isEmpty(codes) && defaultMessage.equals(codes[0])) {
                return defaultMessage;
            }
            return renderDefaultMessage(defaultMessage, resolvable.getArguments(), locale);
        }
        return (!ObjectUtils.isEmpty(codes) ? getDefaultMessage(codes[0]) : null);
    }

    protected String getDefaultMessage(String code) {
        if (isUseCodeAsDefaultMessage()) {
            return code;
        }
        return null;
    }

    @Override
    protected Object[] resolveArguments(Object[] args, Locale locale) {
        if (ObjectUtils.isEmpty(args)) {
            return super.resolveArguments(args, locale);
        }
        List<Object> resolvedArgs = new ArrayList<>(args.length);
        for (Object arg : args) {
            if (arg instanceof MessageSourceResolvable) {
                resolvedArgs.add(getMessage((MessageSourceResolvable) arg, locale));
            } else {
                resolvedArgs.add(arg);
            }
        }
        return resolvedArgs.toArray();
    }

    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        MessageFormat messageFormat = resolveCode(code, locale);
        if (messageFormat != null) {
            synchronized (messageFormat) {
                return messageFormat.format(new Object[0]);
            }
        }
        return null;
    }

    protected abstract MessageFormat resolveCode(String code, Locale locale);

}
